Add marimo MNIST + W&B Registry example - #621
Conversation
Introduces the first marimo notebook in this repo. Trains a small PyTorch CNN on MNIST, logs the run with wandb, saves the trained model as an Artifact, and links it to a W&B Registry collection via the cross-org link_artifact API. The notebook is interactive: hyperparameters live in a marimo form and training is gated by an explicit Train button so slider tweaks do not trigger runs. PEP 723 inline script metadata makes it self-installing under `uvx marimo edit --sandbox`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Active voice, present tense, plain English, and consistent brand capitalization across the README and the notebook's prose cells. Code identifiers wrapped in backticks where missing; a sentence fragment in the verify step rewritten as a complete clause. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
All alerts resolved. Learn more about Socket for GitHub. This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored. |
Mirrors the existing "Get Colabs" PR-comment flow (which serves .ipynb files via nb_helpers) for marimo .py notebooks, which nb_helpers does not handle. On each PR that touches a .py file, the job detects marimo notebooks by their `marimo.App(` marker and upserts a single comment linking each to molab — marimo's hosted runtime that loads any public notebook on GitHub. Uses pull_request_target with pull-requests:write so the comment also lands on fork PRs, and never checks out or runs PR code (reads file content as text only). Links pin to the head commit SHA because branch names with slashes make the molab blob/<ref>/<path> split ambiguous. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
| Notebook | molab |
|---|---|
examples/marimo/mnist-registry/mnist_registry.py |
Links track the head of jmulhausen/marimo-mnist-registry-example.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds a masked mo.ui.text(kind="password") field so users can authenticate by pasting a key instead of running `wandb login` in a shell. The resolved value is kept out of the run config (never logged) and feeds wandb.login(key=...) in the gated training cell; blank falls back to ambient auth (shell login, WANDB_API_KEY, or netrc). Downstream cells read the key via the returned wandb_api_key. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Triage: Socket license alerts on
|
The link step's "common causes" did not name the most common real failure: a view-only org seat (`view-only member cannot write to project`), where the run and artifact succeed but linking is blocked. Lead the remediation with that case and how to resolve it, and note the write-access requirement up front in the prerequisites. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GitHub resolves multi-segment (slashed) branch names in blob/<ref>/<path> and molab fetches from GitHub, so a branch-based link points at the latest revision and needs no per-commit comment maintenance. Content detection still pins to the head SHA. Skip the comment write when the body is unchanged so no-op pushes don't churn it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The view-only link failure now names the required role (Member) and the
three ways an admin can grant it: the Registry Members UI, the Python SDK
(wandb.Api().registry(...) then add_member()/update_member()), and SCIM
(PATCH /scim/Users/{id} with registryRoles), with a link to the configure
registry access docs. README prerequisite updated to match.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The bare molab GitHub URL renders a static preview. Appending /server opens the notebook in a hosted runtime so reviewers can actually run it — needed here since the notebook depends on torch, which the in-browser /wasm mode cannot run. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds a push-triggered workflow that runs `marimo export md` on each notebook under examples/marimo/, writing a peer <notebook>.md, and commits the result back to the branch so the rendered Markdown always tracks the notebook. marimo is pinned for byte-deterministic output (no version-only churn), and three guards prevent commit loops (GITHUB_TOKEN pushes don't re-trigger, *.py-only trigger vs *.md-only commits, and [skip ci]). Includes the initial export of mnist_registry.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Collapses 19 cells into 5, breaking only where marimo requires it or where the user actually provides input: - intro (markdown) - setup + form: imports, device detection, and all input widgets in one cell - train button: its own cell (a widget must be defined separately from the cell that reads its value) - run training: one gated cell that configs, authenticates, inits, builds and trains the model, logs, saves, and links the artifact — streaming each milestone with mo.output.append so it isn't a black box - verify + next steps (markdown), rendered once a run exists run_button.value resets to False after its cascade, so editing the form after a run re-runs the training cell but mo.stop halts it immediately — no silent retrain. Regenerates the Markdown peer. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Socket re-scan
|
There was a problem hiding this comment.
Pull request overview
Adds a new examples/marimo/ MNIST notebook demonstrating training a small PyTorch CNN, logging to W&B, and linking a logged model artifact into a W&B Registry collection, plus GitHub Actions automation for exporting marimo notebooks to Markdown and for posting molab “run this notebook” PR links.
Changes:
- Add a self-contained marimo MNIST → W&B Artifact → W&B Registry notebook (+ exported Markdown peer) under
examples/marimo/mnist-registry/. - Add docs and a
requirements.txtmirror for the notebook’s PEP 723 dependency block. - Add GitHub Actions workflows to (a) auto-export marimo notebooks to Markdown on push and (b) comment molab links on PRs.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
examples/marimo/mnist-registry/mnist_registry.py |
New marimo notebook implementing training, W&B logging, artifact creation, and registry linking. |
examples/marimo/mnist-registry/mnist_registry.md |
Exported Markdown version of the notebook for static rendering. |
examples/marimo/mnist-registry/README.md |
Run instructions, prerequisites, and design notes for the example. |
examples/marimo/mnist-registry/requirements.txt |
Pip requirements mirroring the PEP 723 dependency block. |
.github/workflows/marimo_export_md.yml |
Workflow to export examples/marimo/**/*.py notebooks to peer .md files and commit updates. |
.github/workflows/marimo_molab.yml |
Workflow to post/update PR comments with molab links for modified marimo notebooks. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Remove the unused `import os` and drop `os` from the setup cell's returns. - Compute final_acc from the full-precision last-epoch test_acc instead of the display-rounded history entry, so artifact `test_accuracy` keeps full precision. - marimo_molab.yml: use `**/*.py` (canonical glob) instead of `**.py` so the path filter reliably matches notebooks in subdirectories. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reorders the notebook to: intro -> setup+form -> training pipeline -> Train button -> verify. The button still gates the pipeline (so it doesn't run on open), but now reads as the explicit "run the code above" trigger. The training cell's pre-run message explains what it does and points to the button below. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The entity form field read just "W&B entity", which reads as jargon. Label it "username or team" to match the prerequisites wording. Also tighten the registry-link remediation: an entity is a team or username (not an org), so it now reads "set W&B entity to a team in an org where you have Registry write access." Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Personal-username entities were removed for W&B accounts created after 2024-05-21, so a run's entity must be a team. Relabel the entity field accordingly, fix the prerequisite, and wrap wandb.init so an "entity ... not found" failure renders actionable guidance (set the entity to a team) instead of a raw CommError traceback. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pulls the CNN definition into its own cell so it can be shared, then adds a cell that downloads the model from W&B (preferring the registered version, falling back to the run's artifact so it works even when the registry link is blocked), loads the weights into a fresh network, and classifies 10 held-out MNIST test digits — rendering each as an image with predicted vs. true label and an N/10 correct tally. No new dependency (numpy ships with torch; mo.image renders the arrays). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The consume cell reused `correct` and `pred`, which the training cell already defines — marimo forbids defining a name in more than one cell, so the cell refused to run. Rename them to `n_correct` / `prediction` (both cell-local to the consume step). marimo export does not enforce the single-definition rule, only the kernel does, so this slipped through earlier validation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
) * Refactor the mnist notebook for readability and usability - Move imports and constants into a setup cell - Wrap the UI elements in a submittable form, removing the ui.run_button() - Refactor the monolithic training and logging cell into multiple helper functions, each declared in their own cell as a reusable function - Separate logic cells from view cells when possible - Clean up the evaluation presentation, using a table instead of markdown+emoji - Add a mo.outline() so users can see an outline of the notebook at a glance Core principles: * Gate execution once on mo.stop(), then let the graph handle the rest. All other cells reference run/config, which don't exist until the form is submitted. This lets us remove lots of confusing conditions. * Separate logic from presentation * Push temporary variables into functions to reduce notebook globals. marimo notebooks work best when there are as few global variables as possible. * docs: export marimo notebook(s) to Markdown [skip ci] --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
HiveMind Sessions1 session · 45m · $41
View all sessions in HiveMind → Run |
W&B groups panels by the prefix before "/", so train/* and test/* produced
separate sections, and the example images were logged from normalized tensors
(values outside [0,1]) so they rendered black. Log train loss and test
accuracy under a single "Training/" prefix, drop the redundant test/loss, the
bare "epoch" metric, the broken "examples" panel, and wandb.watch (which added
a separate gradients section). Result: one "Training" section with a loss
chart and an accuracy chart. Also restore the verify cell's f-string
interpolation (it had regressed to literal {run.name}) and update its panel
references.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bring back example predictions the right way: a wandb.Table of un-normalized digit images with true/predicted/confidence, logged once (no per-step slider). Add a confusion matrix so "accuracy" is concrete — which digits the model nails and which it confuses. Both log under the Training prefix after training. Update the intro and verify narration to define accuracy and describe the new panels. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Expand log_eval_report to also log a per-digit PR curve and penultimate-layer embeddings (Net.features) for W&B's 2D projector — one cluster per digit, colored by label. Set run.summary with final/best accuracy and parameter count so they surface on the run overview. Document how to add the 2D Projection panel in the verify steps. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Note: I was just having my agent extract Marimo-specific constraints and style choices from this PR, and it flagged something:
Worth running a final agent check to ensure the README caught up with all the changes made and that it's internally consistent; you can ping me for a re-review if you end up changing anything. |
|
The clean notebooks check is probably not relevant here. It is supposed to clear metadata, cells outputs from a notebook. (But we're converting to a Py script.) |
The README had drifted: it still described wandb.watch gradient histograms and a 16-image prediction gallery, both removed. Update "What you get" to the actual W&B report (Training loss/accuracy, confusion matrix, PR curve, predictions table, 2D-projector embeddings, run-overview headline metrics), describe the in-notebook Evaluation step, add the team-entity prerequisite, and reword the gating note for the marimo form. An agent audit confirms README<->notebook consistency. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@thedriftofwords good catch — fixed the README drift in 9c104aa. Dropped the stale 🤖 Addressed by Claude Code |
|
Agreed, @ngrayluna — that check runs 🤖 Addressed by Claude Code |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (8)
examples/marimo/mnist-registry/mnist_registry.py:582
wandb.define_metric("epoch")(and epoch-based charts) only work if eachwandb.logcall includes anepochvalue. Right now loss logs omitepoch, so the x-axis will remain W&B's default step counter.
if batch_idx % 50 == 0:
wandb.log({"Training/loss": loss.item()})
examples/marimo/mnist-registry/mnist_registry.py:683
- Accuracy logs also need to include the current
epochvalue; otherwisewandb.define_metric(... step_metric="epoch")won’t affect the chart x-axis.
wandb.log({"Training/accuracy": test_acc})
examples/marimo/mnist-registry/mnist_registry.py:289
- The notebook claims (in the PR description) to use
wandb.define_metricwithepochas the x-axis, but the run initialization never callswandb.define_metric. Add metric definitions right afterwandb.init()so subsequent logs can attach toepoch.
This issue also appears in the following locations of the same file:
- line 581
- line 683
run = wandb.init(
project=cfg["project"] or None,
entity=cfg["entity"] or None,
name=cfg["run_name"] or None,
config=config,
job_type="train",
)
examples/marimo/mnist-registry/mnist_registry.py:4
- The inline dependency block declares
marimo>=0.9, but this notebook was generated with marimo0.23.11(__generated_with) and the repo workflow pins0.23.9for exports. The minimum marimo version should be aligned to the generated/pinned version so users don’t end up installing an older incompatible marimo release.
# "marimo>=0.9",
examples/marimo/mnist-registry/requirements.txt:3
requirements.txtshould stay in sync with the PEP 723 dependency block;marimo>=0.9is much lower than the marimo version this notebook was generated/exported with. Bump the minimum marimo version to match the notebook’s__generated_with/ export pin.
marimo>=0.9
examples/marimo/mnist-registry/mnist_registry.md:3
- This exported Markdown front matter records
marimo-version: 0.23.9, but the source notebook advertises__generated_with = "0.23.11". Align the export version with the notebook’s generated version (by bumping the exporter pin and re-exporting) so the repo stays byte-deterministic and version-consistent.
marimo-version: 0.23.9
.github/workflows/marimo_molab.yml:107
- This workflow builds a Markdown table row using untrusted PR-controlled values (
path,headRef). Unescaped backticks/pipes can break the table or inject unintended Markdown, and unencoded characters (e.g.,#, spaces) can produce broken molab URLs. Escape Markdown metacharacters in the rendered path and URL-encode ref/path components when constructing the molab link.
const rows = notebooks.map((path) => {
// The `/server` suffix opens the notebook in a hosted runtime;
// without it molab shows a static, non-runnable preview.
const url = `https://molab.marimo.io/github/${headOwner}/${headRepo}/blob/${headRef}/${path}/server`;
return `| \`${path}\` | [](${url}) |`;
.github/workflows/marimo_export_md.yml:31
MARIMO_VERSIONis pinned to0.23.9, but the notebook being exported declares__generated_with = "0.23.11". Pinning the exporter to the same version avoids version skew and reduces the chance that a future export changes output formatting unexpectedly.
MARIMO_VERSION: "0.23.9"
Summary
Adds the first marimo notebook to this repo at
examples/marimo/mnist-registry/. It trains a small PyTorch CNN on MNIST, produces a rich Weights & Biases run, saves the model as an Artifact, links it into a W&B Registry collection, and then consumes the registered model to classify held-out digits. Two small GitHub Actions workflows support the example.Dependencies are declared in a PEP 723 inline block, so
uvx marimo edit mnist_registry.py --sandboxself-installs them. No new top-level deps for the repo.What the notebook demonstrates
mo.ui.formcollects hyperparameters and W&B targets; training runs only when you submit it with Train model (editing a field never kicks off a run).run.link_artifact(target_path="wandb-registry-<registry>/<collection>"), withlogged.wait()before linking and atry/exceptthat surfaces inline remediation (e.g. view-only-seat guidance) instead of crashing — the run and artifact still succeed.wandb.init, so re-submitting the form never nests runs.The notebook is organized into helper functions via marimo's
@app.functionfor readability.Supporting CI
.github/workflows/marimo_export_md.yml— on push, exports each marimo notebook to a peer Markdown file and commits it, so the notebook renders as a reviewable diff on GitHub..github/workflows/marimo_molab.yml— posts a PR comment linking each modified marimo notebook to molab to run it in a hosted environment.Files
examples/marimo/mnist-registry/mnist_registry.py— the notebookexamples/marimo/mnist-registry/mnist_registry.md— its exported Markdown peer (kept in sync by CI)examples/marimo/mnist-registry/README.md— prereqs, how to run, design notesexamples/marimo/mnist-registry/requirements.txt— pip mirror of the PEP 723 block.github/workflows/marimo_export_md.yml,.github/workflows/marimo_molab.ymlTest plan
uvx marimo edit examples/marimo/mnist-registry/mnist_registry.py --sandboxopens with all cells rendering; the form is interactive and does not train until Train model is submitted.mnist-cnn-<run-id>model Artifact is logged and linked towandb-registry-model/MNIST Classifiers; the Evaluation cell classifies 10 test digits.🤖 Generated with Claude Code